package model;

import FileIO.AppConfig;
import FileIO.HidratadorLaberinto;
import FileIO.PersistenciaLaberinto;
import FileIO.PersistenciaPersonajes;

import java.util.ArrayList;
import java.util.List;

public class Escenario{

    private ArrayList<ArrayList<ElemEscenario>> laberinto;
    private Entero y;
   	private Entero x;
    private String nodoAncho;
    private String nodoAlto;
    private ElemEscenario posicionInicialPacman;
    private ElemEscenario posicionGuaridaFantasmas;
    private Pacman pacman;
    private List<Fantasma> fantasmas; //Cambiar a un mapa de Fantasma,int para manejar los turnos...
    private MovedorMovible movedorMovible;
    public static Integer tiempoFantasmaPresa;
    public static Integer tiempoFantasmaMuerte;
    public static Integer tiempoPacmanAgresivo;

    public Escenario() {
        this.laberinto = new ArrayList<ArrayList<ElemEscenario>>();
        tiempoFantasmaPresa = AppConfig.getInstance().getTiempoPresa();
        tiempoFantasmaMuerte = AppConfig.getInstance().getTiempoMuerto();
        tiempoPacmanAgresivo = tiempoFantasmaPresa;
    }
    
    public String getNodoAncho() {
        return nodoAncho;
    }

    public void setPosicionInicialPacman(ElemEscenario posicionInicialPacman) {
		this.posicionInicialPacman = posicionInicialPacman;
	}

	public void setPosicionGuaridaFantasmas(ElemEscenario posicionGuaridaFantasmas) {
		this.posicionGuaridaFantasmas = posicionGuaridaFantasmas;
	}

	public String getNodoAlto() {
        return nodoAlto;
    }

    public ArrayList<ArrayList<ElemEscenario>> getLaberinto() {
        return laberinto;
    }
        
    public void buildEscenarioDesdeArchivo() {
    	

        y = new Entero(0);
    	x = new Entero(0);
        Cadena nodoAltoAux = new Cadena();
        Cadena nodoAnchoAux = new Cadena();
        posicionInicialPacman = new Camino(0,0);
    	posicionGuaridaFantasmas = new Camino(0,0,true);

    	laberinto = HidratadorLaberinto.getInstance().crearEscenario(y,x,this.posicionInicialPacman,this.posicionGuaridaFantasmas,nodoAltoAux,nodoAnchoAux);
        nodoAlto = nodoAltoAux.stringValue();
        nodoAncho = nodoAnchoAux.stringValue();

    }

    public void buildEscenarioPrueba() {
        //POR DEFAULT EL CAMINO INSTANCIA UNA BOLITA
        this.y = new Entero(12);
        this.x = new Entero(13);

        //Escenario de prueba
        //   0-1-2-3-4-5-6-7-8-9-10-11-12
        //0  p-p-p-p-p-p-t-p-p-p- p- p- p
        //1  p-p-p-p-p-p-c-p-p-p- p- p- p
        //2  p-p-p-C-p-p-c-p-p-C- p- p- p
        //3  t-c-c-c-c-c-c-c-c-c- c- c- t
        //4  p-p-p-p-c-p-p-p-c-p- p- p- p
        //5  p-p-c-c-c-p-F-p-c-c- c- p- p
        //6  p-p-c-p-c-c-c-c-c-p- c- p- p
        //7  p-p-c-c-c-p-p-p-c-c- c- p- p
        //8  t-p-c-p-c-c-I-c-c-p- c- c- t
        //9  p-p-C-c-c-p-c-p-c-c- C- p- p
        //10 p-p-p-p-p-p-c-p-p-p- p- p- p
        //11 p-p-p-p-p-p-t-p-p-p- p- p- p
        //

        //Columna 0
        ArrayList<ElemEscenario> columna = new ArrayList<ElemEscenario>();
        columna.add(new Pared(0, 0));
        columna.add(new Pared(1, 0));
        columna.add(new Pared(2, 0));
        columna.add(new Transportador(3, 0, 3, 11));
        columna.add(new Pared(4, 0));
        columna.add(new Pared(5, 0));
        columna.add(new Pared(6, 0));
        columna.add(new Pared(7, 0));
        columna.add(new Transportador(8, 0, 8, 11));
        columna.add(new Pared(9, 0));
        columna.add(new Pared(10, 0));
        columna.add(new Pared(11, 0));
        laberinto.add(columna);

        //Columna 1
        columna = new ArrayList<ElemEscenario>();
        columna.add(new Pared(0, 1));
        columna.add(new Pared(1, 1));
        columna.add(new Pared(2, 1));
        columna.add(new Camino(3, 1));
        columna.add(new Pared(4, 1));
        columna.add(new Pared(5, 1));
        columna.add(new Pared(6, 1));
        columna.add(new Pared(7, 1));
        columna.add(new Camino(8, 1));
        columna.add(new Pared(9, 1));
        columna.add(new Pared(10, 1));
        columna.add(new Pared(11, 1));
        laberinto.add(columna);
        
        //Columna 2
        columna = new ArrayList<ElemEscenario>();
        columna.add(new Pared(0, 2));
        columna.add(new Pared(1, 2));
        columna.add(new Pared(2, 2));
        columna.add(new Camino(3, 2));
        columna.add(new Pared(4, 2));
        columna.add(new Camino(5, 2));
        columna.add(new Camino(6, 2));
        columna.add(new Camino(7, 2));
        columna.add(new Camino(8, 2));
        columna.add(new Camino(9, 2, new Bolon()));
        columna.add(new Pared(10, 2));
        columna.add(new Pared(11, 2));
        laberinto.add(columna);

        //Columna 3
        columna = new ArrayList<ElemEscenario>();
        columna.add(new Pared(0,3));
        columna.add(new Pared(1, 3));
        columna.add(new Camino(2,3, new Bolon()));
        columna.add(new Camino(3,3));
        columna.add(new Pared(4,3));
        columna.add(new Camino(5,3));
        columna.add(new Pared(6,3));
        columna.add(new Camino(7,3));
        columna.add(new Pared(8,3));
        columna.add(new Camino(9,3));
        columna.add(new Pared(10, 3));
        columna.add(new Pared(11,3));
        laberinto.add(columna);

        //Columna 4
        columna = new ArrayList<ElemEscenario>();
        columna.add(new Pared(0,4));
        columna.add(new Pared(1, 4));
        columna.add(new Pared(2,4));
        columna.add(new Camino(3,4));
        columna.add(new Camino(4,4));
        columna.add(new Camino(5,4));
        columna.add(new Camino(6,4));
        columna.add(new Camino(7,4));
        columna.add(new Camino(8,4));
        columna.add(new Camino(9,4));
        columna.add(new Pared(10, 4));
        columna.add(new Pared(11,4));
        laberinto.add(columna);

        //Columna 5
        columna = new ArrayList<ElemEscenario>();
        columna.add(new Pared(0,5));
        columna.add(new Pared(1, 5));
        columna.add(new Pared(2,5));
        columna.add(new Camino(3,5));
        columna.add(new Pared(4,5));
        columna.add(new Pared(5,5));
        columna.add(new Camino(6,5));
        columna.add(new Pared(7,5));
        columna.add(new Camino(8,5));
        columna.add(new Pared(9,5));
        columna.add(new Pared(10, 5));
        columna.add(new Pared(11,5));
        laberinto.add(columna);

        //Columna 6
        columna = new ArrayList<ElemEscenario>();
        columna.add(new Transportador(0,6, 10,6));
        columna.add(new Camino(1, 6));
        columna.add(new Camino(2,6));
        columna.add(new Camino(3,6));
        columna.add(new Pared(4,6));
        posicionGuaridaFantasmas = new Camino(5,6, true);
        columna.add(posicionGuaridaFantasmas);
        columna.add(new Camino(6, 6));
        columna.add(new Pared(7,6));
        posicionInicialPacman = new Camino(8,6);
        columna.add(posicionInicialPacman);
        columna.add(new Camino(9,6));
        columna.add(new Camino(10, 6));
        columna.add(new Transportador(11,6, 1,6));
        laberinto.add(columna);

        //Columna 7
        columna = new ArrayList<ElemEscenario>();
        columna.add(new Pared(0,7));
        columna.add(new Pared(1, 7));
        columna.add(new Pared(2,7));
        columna.add(new Camino(3,7));
        columna.add(new Pared(4,7));
        columna.add(new Pared(5,7));
        columna.add(new Camino(6,7));
        columna.add(new Pared(7,7));
        columna.add(new Camino(8,7));
        columna.add(new Pared(9,7));
        columna.add(new Pared(10, 7));
        columna.add(new Pared(11,7));
        laberinto.add(columna);

        //Columna 8
        columna = new ArrayList<ElemEscenario>();
        columna.add(new Pared(0,8));
        columna.add(new Pared(1, 8));
        columna.add(new Pared(2,8));
        columna.add(new Camino(3,8));
        columna.add(new Camino(4,8));
        columna.add(new Camino(5,8));
        columna.add(new Camino(6,8));
        columna.add(new Camino(7,8));
        columna.add(new Camino(8,8));
        columna.add(new Camino(9,8));
        columna.add(new Pared(10, 8));
        columna.add(new Pared(11,8));
        laberinto.add(columna);

        //Columna 9
        columna = new ArrayList<ElemEscenario>();
        columna.add(new Pared(0,9));
        columna.add(new Pared(1, 9));
        columna.add(new Camino(2,9, new Bolon()));
        columna.add(new Camino(3,9));
        columna.add(new Pared(4,9));
        columna.add(new Camino(5,9));
        columna.add(new Pared(6,9));
        columna.add(new Camino(7,9));
        columna.add(new Pared(8,9));
        columna.add(new Camino(9, 9));
        columna.add(new Pared(10, 9));
        columna.add(new Pared(11,9));
        laberinto.add(columna);

        //Columna 10
        columna = new ArrayList<ElemEscenario>();
        columna.add(new Pared(0,10));
        columna.add(new Pared(1,10));
        columna.add(new Pared(2,10));
        columna.add(new Camino(3,10));
        columna.add(new Pared(4,10));
        columna.add(new Pared(5,10));
        columna.add(new Camino(6,10));
        columna.add(new Camino(7,10));
        columna.add(new Camino(8,10));
        columna.add(new Camino(9,10, new Bolon()));
        columna.add(new Pared(10, 10));
        columna.add(new Pared(11,10));
        laberinto.add(columna);

        //Columna 11
        columna = new ArrayList<ElemEscenario>();
        columna.add(new Pared(0,11));
        columna.add(new Pared(1, 11));
        columna.add(new Pared(2,11));
        columna.add(new Camino(3,11));
        columna.add(new Pared(4,11));
        columna.add(new Pared(5,11));
        columna.add(new Pared(6,11));
        columna.add(new Pared(7,11));
        columna.add(new Camino(8,11));
        columna.add(new Pared(9,11));
        columna.add(new Pared(10, 11));
        columna.add(new Pared(11,11));
        laberinto.add(columna);

        //Columna 12
        columna = new ArrayList<ElemEscenario>();
        columna.add(new Pared(0,12));
        columna.add(new Pared(1,12));
        columna.add(new Pared(2,12));
        columna.add(new Transportador(3,12, 3,1));
        columna.add(new Pared(4,12));
        columna.add(new Pared(5,12));
        columna.add(new Pared(6,12));
        columna.add(new Pared(7,12));
        columna.add(new Transportador(8,12, 8, 1));
        columna.add(new Pared(9,12));
        columna.add(new Pared(10, 12));
        columna.add(new Pared(11,12));
        laberinto.add(columna);
    }

    public void buildPersonajesEscenario() {
        pacman = new Pacman(posicionInicialPacman);
        fantasmas = new ArrayList<Fantasma>();
        crearFantasma(new Zonzo());
        crearFantasma(new Perezozo());
        crearFantasma(new Buscador());
        crearFantasma(new BuscadorTemperamental());
    }

    private void crearFantasma(Vivacidad vivacidad) {
        Fantasma fantasma = new Fantasma(posicionGuaridaFantasmas, vivacidad);
        fantasmas.add(fantasma);
        pacman.addObserver(fantasma);
    }


    private boolean moverPacmanIzquierda(int turno) {
        movedorMovible = new MovedorPacman();
        boolean b = false;
        if (seTieneQueMover(pacman, turno)) {
            b = movedorMovible.movermovibleIzquierda(this, pacman);
        }
        return b;
    }

    private boolean moverPacmanDerecha(int turno) {
        movedorMovible = new MovedorPacman();
        boolean b = false;
        if (seTieneQueMover(pacman, turno)) {
            b = movedorMovible.movermovibleDerecha(this, pacman);
        }
        return b;
    }

    private boolean moverPacmanArriba(int turno) {
        movedorMovible = new MovedorPacman();
        boolean b = false;
        if (seTieneQueMover(pacman, turno)) {
            b = movedorMovible.movermovibleArriba(this, pacman);
        }
        return b;
    }

    private boolean moverPacmanAbajo(int turno) {
        movedorMovible = new MovedorPacman();
        boolean b = false;
        if (seTieneQueMover(pacman, turno)) {
            b = movedorMovible.movermovibleAbajo(this, pacman);
        }
        return b;
    }

    public boolean moverPacman(int turno,String movPacman){
        if (movPacman.equals(Constantes.IZQUIERDA)) {
            return moverPacmanIzquierda(turno);
        } else if (movPacman.equals(Constantes.DERECHA)) {
            return moverPacmanDerecha(turno);
        } else if (movPacman.equals(Constantes.ARRIBA)) {
            return moverPacmanArriba(turno);
        } else if (movPacman.equals(Constantes.ABAJO)) {
            return moverPacmanAbajo(turno);
        }
        return false;
    }

    /**
     * Si todos los fantasmas se pueden mover retorna true, caso contrario retorna false.
     * @param turno
     * @return
     */
    public boolean moverFantasmas(int turno) {
        movedorMovible = new MovedorFantasma();
        boolean ret_val=true;
        for (Fantasma fantasma : fantasmas) {
            if (seTieneQueMover(fantasma, turno)) {
                ret_val=(movedorMovible.mover(this, fantasma)==false)?false:ret_val;
            }
        }
        return ret_val;
    }

    private boolean seTieneQueMover(Movible movible, int turno) {
        //Velocidad (1: se mueve cada 3 turnos ,2: se mueve cada 2,3: se mueve todos los turnos)
        if (movible.getVelocidad() == 0) return false;
        if (movible.getVelocidad() == 3) return true;

        int turnoInicioMovimiento = movible.getTurnoIncioMovimiento();
        int cantTurnosSaltos = 0;
        if (movible.getVelocidad() == 2) cantTurnosSaltos = 2;
        else if (movible.getVelocidad() == 1) cantTurnosSaltos = 3;

        int dif = turno - turnoInicioMovimiento;
        while (dif > 0) {
            dif -= cantTurnosSaltos;
        }

        return dif == 0;

    }

    public ElemEscenario getPosicionGuaridaFantasmas() {
        return posicionGuaridaFantasmas;
    }

    public ElemEscenario getPosicionInicialPacman() {
        return posicionInicialPacman;
    }

    public int getX() {
        return x.intValue();
    }

    public int getY() {
        return y.intValue();
    }

    public void setLaberinto(ArrayList<ArrayList<ElemEscenario>> laberinto) {
		this.laberinto = laberinto;
	}

	public ElemEscenario getPosicionActualPacman(){
        return pacman.getPosicion();
    }

    public boolean actualizarEstado() {
        if (!checkearFinDeJuego()) {
            pacman.comer(pacman.getPosicion().getItem());
            if (pacman.estaAgresivo()) {
                for (Fantasma fantasma : fantasmas) {
                    if (pacman.getPosicion() == fantasma.getPosicion() && fantasma.morir()) {
                        fantasma.iniciarTemporizador(Escenario.tiempoFantasmaMuerte);
                    }
                }
            }
            aumentarAgresividadFantasmasSegunTurno();
        }
        return true;
    }

    private void aumentarAgresividadFantasmasSegunTurno() {
        int turno = Juego.getInstance().getTurno();
        //Cada 15 turnos los fantasmas aumentan su agresividad
        if (turno != 0 && turno % Constantes.CANTIDAD_TURNOS_AUMENTO_AGRESIVIDAD == 0) {
            for (Fantasma fantasma : fantasmas) {
                fantasma.molestar();
            }
        }
    }

    public boolean checkearFinDeJuego() {
        //Muerte del pacman, perdio pacman, fin!
        for (Fantasma fantasma : fantasmas) {
            if (pacman.getPosicion().equals(fantasma.getPosicion()) && !fantasma.esPresa()) {
                return true;
            }
        }

        //Chekeo Bolitas comidas! Si queda alguna sin comer, no es el fin!
        for (ArrayList<ElemEscenario> lista: laberinto){
            for (ElemEscenario elemEscenario: lista){
                Item item = elemEscenario.getItem();
                if (elemEscenario.getClass().equals(Camino.class) &&
                        item != null &&
                        item.getClass().equals(Bolita.class) &&
                        !item.fuiComido()){
                    return false;
                }
            }
        }

        //Todas las bolitas fueron comidas, gano pacman, fin!
        return true;
    }

    public int getCuadrante(ElemEscenario posicion) {
        // 1 - 2
        // 3 - 4
        int xMid = x.intValue() / 2;
        int yMid = y.intValue() / 2;
        if (posicion.getX() > 0 && posicion.getX() <= xMid && posicion.getY() > 0 && posicion.getY() <= yMid) return 1;
        if (posicion.getX() > xMid && posicion.getX() <= x.intValue() && posicion.getY() > 0 && posicion.getY() <= yMid) return 2;
        if (posicion.getX() > 0 && posicion.getX() <= xMid && posicion.getY() > yMid && posicion.getY() <= y.intValue()) return 3;
        return 4;
    }

    public void generarArchivoSalidaConEstadoActual() {
        PersistenciaLaberinto.getInstance().persistir(this);
        PersistenciaPersonajes.getInstance().persistir(this);
    }

    public ElemEscenario getPosicion(int y, int x) {
        for (ArrayList<ElemEscenario> lista: laberinto){
            for (ElemEscenario elemEscenario : lista){
                if (elemEscenario.getX() == x && elemEscenario.getY() == y){
                    return elemEscenario;
                }
            }
        }
        return null;
    }

	public List<Fantasma> getFantasmas() {
		return this.fantasmas;
	}

	public Pacman getPacMan() {
		return this.pacman;
	}
	
	public void setY(Entero y) {
		this.y = y;
	}

	public void setX(Entero x) {
		this.x = x;
	}
}
